#ifndef PW_PBL_META_VHARRAY_HPP
#define PW_PBL_META_VHARRAY_HPP

#include <array>
#include "Auto.hpp"
#include "VHData.hpp"

namespace pw_pbl::Meta
{
	template <typename ELEMENT, auto... PARMS> requires(sizeof...(PARMS) <= 2)
	struct VHArray;

	template <typename ELEMENT>
	struct VHArray<ELEMENT>
	{
		inline _vhdata::Header<ELEMENT> &header() noexcept { return *reinterpret_cast<_vhdata::Header<ELEMENT> *>(this); }
		inline const _vhdata::Header<ELEMENT> &header() const noexcept { return *reinterpret_cast<const _vhdata::Header<ELEMENT> *>(this); }

		inline size_t size() const noexcept { return header().value(); }
		inline size_t headerSize() const noexcept { return header().size(); }
		inline auto *begin() noexcept { return (ELEMENT *)header().end(); }
		inline auto *end() noexcept { return begin() + size(); }
		inline auto *data() noexcept { return begin(); }
		inline auto *begin() const noexcept { return (const ELEMENT *)header().end(); }
		inline auto *end() const noexcept { return begin() + size(); }
		inline auto *data() const noexcept { return begin(); }

		template <std::ranges::sized_range T> requires std::convertible_to<std::ranges::range_value_t<T>, const ELEMENT>
		inline size_t write(T &&view, size_t maxBytesSize) noexcept
		{
			auto sz = std::ranges::size(view) * sizeof(ELEMENT);
			if (sz >= maxBytesSize)
				return 0;
			auto nhdr = header().encode(std::ranges::size(view));
			auto cellCnt = nhdr.size();
			if (cellCnt * nhdr.ECellSize > maxBytesSize - sz)
				return 0;
			header().assign(nhdr);
			std::copy_n(std::ranges::begin(view), sz, (ELEMENT *)(header().data() + cellCnt));
			return sz + cellCnt * nhdr.ECellSize;
		}

	protected:
		VHArray() = default;
		VHArray(VHArray &&) = default;
		VHArray(const VHArray &) = default;
	};

	template <typename ELEMENT, size_t N, bool... PACKED> requires(sizeof...(PACKED) <= 1)
	struct VHArray<ELEMENT, N, PACKED...> : public _vhdata::DeGap<VHArray<ELEMENT>, _vhdata::Header<ELEMENT, N>, std::array<ELEMENT, N>, PACKED...>
	{
		using base = _vhdata::DeGap<VHArray<ELEMENT>, _vhdata::Header<ELEMENT, N>, std::array<ELEMENT, N>, PACKED...>;
		using element_type = ELEMENT;
		static constexpr size_t ESize = N;

		constexpr VHArray(const element_type (&_data)[ESize]) noexcept : base{std::to_array(_data)} {}
		constexpr VHArray(const std::array<element_type, ESize> &_data) noexcept : base{_data} {}

		constexpr operator std::array<element_type, ESize> &() noexcept { return this->data; }
		constexpr operator const std::array<element_type, ESize> &() const noexcept { return this->data; }

		constexpr bool operator==(const VHArray &) const = default;
		constexpr bool operator==(auto &) const { return false; }
	};

	template <typename ELEMENT, size_t N>
	VHArray(const ELEMENT (&)[N]) -> VHArray<ELEMENT, N>;

	template <typename ELEMENT, size_t N>
	VHArray(const std::array<ELEMENT, N> &) -> VHArray<ELEMENT, N>;

	template <typename ELEMENT = void>
	struct VHArrayPtr
	{
		static constexpr auto EIsConst = std::is_const_v<ELEMENT>;
		using TArray = std::conditional_t<EIsConst, const VHArray<std::remove_const_t<ELEMENT>>, VHArray<ELEMENT>>;

		union
		{
			TArray *pArray;
			std::conditional_t<EIsConst, const void, void> *pVoid;
		};

		constexpr VHArrayPtr(TArray &raw) noexcept : pArray(&raw) {}

		constexpr TArray &operator*() const noexcept { return *pArray; }
		constexpr TArray *operator->() const noexcept { return pArray; }
		explicit constexpr operator bool() const noexcept { return pArray != nullptr; }

		/// 从任意指针构造vl指针，注意必须确认指针指向的布局为VLArray（vl头+纯数组）
		template <typename T> requires(alignof(T) <= alignof(size_t))
		explicit constexpr VHArrayPtr(T *p) noexcept : pVoid(p) {}

		constexpr size_t size() const noexcept { return pArray->size(); }
		constexpr auto *begin() const noexcept { return pArray->begin(); }
		constexpr auto *end() const noexcept { return begin() + size(); }
		constexpr auto *data() const noexcept { return begin(); }
	};

	template <typename ELEMENT>
	VHArrayPtr(VHArray<ELEMENT> &) -> VHArrayPtr<ELEMENT>;

	template <typename ELEMENT>
	VHArrayPtr(const VHArray<ELEMENT> &) -> VHArrayPtr<const ELEMENT>;

	inline namespace literals
	{
		template <typename T = Auto<>, auto... v>
		extern constexpr auto MetaVHArray = VHArray{std::array{v...}};

		template <auto DATA> requires requires { VHArray{DATA}; }
		extern constexpr auto MetaVHArray<Auto<>, DATA> = VHArray{DATA};

		template <typename T, auto... v> requires(!std::same_as<T, Auto<>>)
		extern constexpr VHArray<T, sizeof...(v)> MetaVHArray<T, v...> = std::array<T, sizeof...(v)>{v...};
	} // namespace literals

} // namespace pw_pbl::Meta

#endif // PW_PBL_META_VHARRAY_HPP
